# -*- coding: utf-8 -*-
"""
Macana XML-RPC Dispatcher for Taíno
Copyright (C) 2009, Joshua "jag" Ginsberg <jag@flowtheory.net

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
    
import sys
import traceback
import xmlrpclib
from django.core import exceptions
from macana import settings

class MacanaXMLRPCDispatcher(object):
    """MacanaXMLRPCDispatcher is an implementation of SimpleXMLRPCDispatcher
    to relay requests to the component system."""
    
    DEFAULT_FAULT_CODE = -32400
    UNAUTHORIZED_FAULT_CODE = -32401
    APPLICATION_FAULT_CODE = -32500
    METHOD_NOT_FOUND_FAULT_CODE = -32601
    INVALID_PARAMETERS_FAULT_CODE = -32602
    DISPATCHER_ERROR_FAULT_CODE = -32603

    def __init__(self, authen_interface, method_interface):
        if settings.DEBUG:
            sys.stderr.write('Initializing dispatcher...\n')
        self.allow_none = True
        self.encoding = 'utf-8'
        self.authen_interface = authen_interface
        self._load_authentication_component()
        self.method_interface = method_interface
    
    def _load_authentication_component(self):
        print 'Reloading authen component'
        self.authen_component = self.authen_interface.get_component_by_id(
            settings.AUTH_COMPONENT)
        if not self.authen_component:
            raise exceptions.ImproperlyConfigured, u'MACANA_AUTH_COMPONENT %s cannot be found.' % settings.AUTH_COMPONENT
        

    def _marshaled_dispatch(self, request):
        settings.DEBUG and sys.stderr.write('Marshalling request...\n')
        data = request.raw_post_data
        settings.DEBUG and sys.stderr.write('Raw data:\n%s\n' % data)
        try:
            params, method = xmlrpclib.loads(data)
            settings.DEBUG and sys.stderr.write('Method: %s\n' % method)
            settings.DEBUG and sys.stderr.write('Params: %s\n' % str(params))
            response = self._dispatch(request, method, params)
            response = (response,)
            response = xmlrpclib.dumps(response, methodresponse=1,
                                       allow_none=self.allow_none,
                                       encoding=self.encoding)
        except xmlrpclib.Fault, fault:
            settings.DEBUG and traceback.print_exc(file=sys.stderr)
            response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
                                       encoding=self.encoding)
        except Exception, e:
            settings.DEBUG and traceback.print_exc(file=sys.stderr)
            response = xmlrpclib.dumps(
                xmlrpclib.Fault(self.APPLICATION_FAULT_CODE, 
                                str(e),
                                encoding=self.encoding, 
                                allow_none=self.allow_none,
                                ))
        settings.DEBUG and sys.stderr.write('Response: %s\n' % response)
        return response
        
    def _dispatch(self, request, method, params):
        matches = filter(lambda tpl: tpl[1] == method,
                         self.method_interface.get_method_name())
        settings.DEBUG and sys.stderr.write('Method matches: %s\n' % str(matches))
        if matches:
            component = self.method_interface.get_component_by_id(
                matches[0][0])
            settings.DEBUG and sys.stderr.write('Component: %s\n' % str(component))
            settings.DEBUG and sys.stderr.write('Authenticating...\n')
            self.authen_component.authenticate(request, method, params)
            settings.DEBUG and sys.stderr.write('Authentication phase complete...\n')
            authorized = component.authorize(request)
            if authorized:
                settings.DEBUG and \
                    sys.stderr.write('Authorized request... dispatching...')
                return component.handle_request(request, *params)
            else:
                settings.DEBUG and \
                    sys.stderr.write('Unauthorized request...')
                raise xmlrpclib.Fault(
                    self.UNAUTHORIZED_FAULT_CODE,
                    self.authen_component.get_instructions()
                    )
        else:
            settings.DEBUG and \
                sys.stderr.write('Unknown method...')
            raise xmlrpclib.Fault(
                self.METHOD_NOT_FOUND_FAULT_CODE,
                u'Method not implemented.'
                )
